#!/usr/bin/env python3
import sys, time
import os, codecs
import logging
from array import array
import PyQt5.sip
import binascii
#========
import serial
import serial.tools.list_ports
from PyQt5.QtCore import  QSettings,QFile, QByteArray, QEvent, pyqtSignal, Qt , QSignalMapper,  \
                                        QTextCodec, QFileInfo, QTimer
#(QFile, QFileInfo, , QSignalMapper,\             QTextCodec, QTimer,Qt,QByteArray, QEvent, )
from PyQt5.QtWidgets import (QAction, QApplication, QFileDialog,QTreeWidget, QDockWidget , QTreeWidgetItem, \
        QSplitter,  QMainWindow, QMessageBox, QTextEdit,QMdiArea,QWidget, QMenu, QActionGroup, QFontDialog)
from PyQt5.QtGui import QIcon,QKeySequence, QCursor, QFont, QCloseEvent
from textedit_py import QtextEditClick, SciTextEdit
import qrc_resources
from Dom_do_cbp import Dom4cbp
from  compileProject  import compileProject
from stcflash import *
from downloadSetting import   Setting
from lexer_c_getfunc import lexer_c_getfunc, g_allFuncList, g_includeList
from _compileSetting import complierSetting
from Ui_FineText import FindText
from    ColorDlg import ColorDlg, g_colorDict
from pyserial_demo import   Pyqt5_Serial
import copy

PROTOCOL_12C5A = "12c5a"
PROTOCOL_15F2K ="15F2K"
PROTOCOL_8F2K ="8F2K"

__version__ = "1.0.0" 
class MainWindow(QMainWindow):
    Jump2Func_Signal = pyqtSignal(str)
    Jump2IncludeFile_Signal = pyqtSignal(str)

    def __init__(self, parent=None):
        global g_colorDict
        super(MainWindow, self).__init__(parent)
        self.isComplierPathDefault='0'
        self.isXdataDefault = '0'
        self.XdataLen = '8192'
        self.downloadWay=''
        self.DIYString=''
        self.baudArray=[9600, 19200, 38400, 57600, 115200]
        self.baudIndex=0
        self.Chipstr=''
        self.Portstr=''
        self.fullfile=''    #project file 
        self.ProjectDir=''
        self.Hexfile=''
        self.file_list = [] #c文件，h文件列表初始化         self.h_list = []   
        #self.includeList=[]
        self.file_list.clear()
        self.fileFuncDict={}
        self.autoOpenCOM='0'
        self.mycbp=''
        self.compileIncldue=''
        self.Code51_dir = os.getcwd().replace("\\", "/").replace("\\", "/")   # 本软件所在的路径
        self.EditFont = QFont('Courier', 12)
        
        self.colorDict=copy.deepcopy(g_colorDict)
        #窗口布局：        
        self.tree=QTreeWidget()        #树状控件，内置于工程视图
        self.tree.setColumnCount(1)       #设置列数 
        self.tree.header().hide()           #隐藏 置树形控件头部的标题，有多列时有用。
        #1.1创建 工程视图（标题，自身窗口），占右边               
        self.dockProject=QDockWidget('Project',self)
        self.dockProject.setWidget(self.tree)
        self.dockProject.setMinimumSize(180,600)  # 设置最小尺寸：宽=180，高=600
         #1.2创建多文档视图
        self.mdi = QMdiArea()       
        self.mdi.setViewMode(QMdiArea.TabbedView) #设置为Tab多页显示模式
        self.mdi.setTabsClosable(1)                         #设置多文档页面可关闭
                
        self.text_browser = QtextEditClick(self) #QtextEditClick继承自QTextEdit，内置于信息输出视图
        self.text_browser.setReadOnly(True)     #仅作为信息输出，设置“只读”属性
        #1.3创建信息输出视图
        self.dockBuilt=QDockWidget('Built output',self)
        self.dockBuilt.setMinimumSize(600,150)     # 宽=600，高=   150  
        self.dockBuilt.setWidget(self.text_browser)
        
        #1.4QSplitter（分隔布局容器）布局分上下（垂直），比例3：1
        splitter1 = QSplitter(Qt.Vertical)
        splitter1.addWidget(self.mdi)           #多文档视图占布局右上
        splitter1.addWidget(self.dockBuilt)     #信息输出视图占布局右下   
        self.addDockWidget(Qt.LeftDockWidgetArea,self.dockProject)#工程视图占布局左边
        self.setCentralWidget(splitter1)     
        #splitter1.setStretchFactor(0, 2)        #splitter1.setStretchFactor(1, 1)
        #2.生成菜单，工具栏
        fileNewAction = self.createAction("&New", self.fileNew,
                QKeySequence.New, "filenew", "Create a text file")
        fileOpenAction = self.createAction("&Open...", self.fileOpen,
                QKeySequence.Open, "fileopen",
                "Open an existing text file")
        fileSaveAction = self.createAction("&Save", self.fileSave,
                QKeySequence.Save, "filesave", "Save the text")
        fileSaveAsAction = self.createAction("Save &As...",
                self.fileSaveAs, icon="filesaveas",
                tip="Save the text using a new filename")
        fileSaveAllAction = self.createAction("Save A&ll",
                self.fileSaveAll, "filesave",
                tip="Save all the files")
        fileQuitAction = self.createAction("&Quit", self.close,
                "Ctrl+Q", "filequit", "Close the application")
                
        editUndoAction = self.createAction("&Copy", self.editUndo,
                QKeySequence.Undo, "undo",
                "Undo")
        editRedoAction = self.createAction("&Redo", self.editRedo,
                QKeySequence.Redo, "redo",
                "Redo")
        editCopyAction = self.createAction("&Copy", self.editCopy,
                QKeySequence.Copy, "editcopy",
                "Copy text to the clipboard")
        editCutAction = self.createAction("Cu&t", self.editCut,
                QKeySequence.Cut, "editcut",
                "Cut text to the clipboard")
        editPasteAction = self.createAction("&Paste", self.editPaste,
                QKeySequence.Paste, "editpaste",
                "Paste in the clipboard's text")
        editFindAction = self.createAction("&Find", self.editFind,
                QKeySequence.Find, 'find',
                "Find text in files")
        editFontAction = self.createAction("&Font Select选字体", self.getFont,
                None, None,
                "Select Font 选择字体")#getFont
                
        projectNewAction =self.createAction("&New", self.ProjectNew, None, None, 
                                                            'Creat a project')
        projectOpenAction =self.createAction("&Open", self.ProjectOpen, None, None, 
                                                            'Open project file')
        projectCloseAction =self.createAction("&Close", self.ProjectClose, None, None, 
                                                            'Close project')
        projectBuiltAction =self.createAction("&Built", self.ProjectBuilt, Qt.Key_F8, "built", 
                                                            'Built 编译 F8')
        projectCleanAction =self.createAction("&Clean", self.ProjectClean, None, "clean", 
                                                            'Clean project')#loadHEX2stc
        projectLoadAction =self.createAction("&Download", self.ProjectDownload, Qt.Key_F9, "download2", 
                                                            'donewload HEX 下载到芯片 F9')#
        projectBuiltLoadAction =self.createAction("Built and &load", self.ProjectBuiltLoad, Qt.Key_F10, "built_down1", 
                                                            '先编译后下载 F10')#
                
        #TabbedAction = self.createAction("tabbed", self.setMdiTabbedWindow)
        self.windowNextAction = self.createAction("&Next",
                self.mdi.activateNextSubWindow, QKeySequence.NextChild)
        self.windowPrevAction = self.createAction("&Previous",
                self.mdi.activatePreviousSubWindow,
                QKeySequence.PreviousChild)
        self.windowCascadeAction = self.createAction("Casca&de",
                self.mdi.cascadeSubWindows)
        self.windowTileAction = self.createAction("&Tile",
                self.mdi.tileSubWindows)
        self.windowTabbedAction = self.createAction("tabbed", self.setMdiTabbedWindow)
        self.windowRestoreAction = self.createAction("&Restore All",
                self.windowRestoreAll)
        self.windowMinimizeAction = self.createAction("&Iconize All",
                self.windowMinimizeAll)
        #self.windowArrangeIconsAction = self.createAction( "&Arrange Icons", self.mdi.arrangeIcons)
        self.windowArrangeIconsAction = self.createAction(
                "&Arrange Icons", self.windowMinimizeAll)        
        self.windowCloseAction = self.createAction("&Close",
                self.mdi.closeActiveSubWindow, QKeySequence.Close) 
        self.windowShowProjectAction = self.createAction("&Project-Viewer",
                self.showProjectWindow, None, None,  checkable=True)
        self.windowShowBuiltAction = self.createAction("&Built-Viewer",
                self.showBuiltWindow, None, None,  checkable=True)
        self.windowMapper = QSignalMapper(self)
        self.windowMapper.mapped[QWidget].connect(self.mdi.setActiveSubWindow)  
        #菜单1.file
        fileMenu = self.menuBar().addMenu("&File")
        
        #“fileMenu” 增加exampleMenu，再增加子菜单，example文件夹下面的每个子文件夹就是一个子菜单
        #子菜单再增加Action，每个子子文件夹对应一个Action（就是工程名）
        exampleMenu = QMenu('example', self)
        fileMenu.addMenu(exampleMenu)
        #0. exampleMenu 上的动作项链接到槽函数 OpenRecentProject
        exampleMenu.triggered[QAction].connect(self.OpenRecentProject)
        if os.path.exists(self.Code51_dir+'/example'):
            #1. 遍历examp 文件夹下面的子文件夹
            for dir in os.listdir(self.Code51_dir+'/example'):  
                if os.path.isfile(dir):     #1.2 为文件 则忽略
                    continue
                #2. 子文件夹也生成一个子子菜单
                exampleSubMenu=QMenu(dir, self)
                exampleMenu.addMenu(exampleSubMenu) 
                fulldir = self.Code51_dir+'/example'+'/'+dir
                #3. 遍历子文件夹里面的子子文件夹，子子文件夹就是工程所在
                for  _dir in os.listdir(fulldir):
                    if os.path.isfile(_dir):
                        continue
                    #            example         dir              _dir              _dir.cbp
                    #3.1 举例 example/01BasicCode/01target001/01target001.cbp
                    sub_fulldir=fulldir+'/'+_dir+'/'+_dir+'.cbp'
                    #4. 将工程名创建为一个动作。
                    if os.path.exists(sub_fulldir):
                        fileAction = self.createAction(sub_fulldir, None, None, None, 
                                                            'Open an example project', checkable=False)
                        exampleSubMenu.addAction(fileAction)        
        #================增加exampleMenu--完毕=====================
        self.addActions(fileMenu, (fileNewAction, fileOpenAction,
                fileSaveAction, fileSaveAsAction, fileSaveAllAction,
                None, fileQuitAction))
        editMenu = self.menuBar().addMenu("&Edit")
        self.addActions(editMenu, (editUndoAction, editRedoAction, editCopyAction, editCutAction,
                                   editPasteAction, editFindAction, editFontAction))#, TabbedAction
                                   
        #创建project Menu        
        projectMenu = self.menuBar().addMenu("&Project")
        self.addActions(projectMenu, (projectNewAction, projectOpenAction, projectCloseAction, 
                                                    projectBuiltAction, projectCleanAction, projectLoadAction, 
                                                    projectBuiltLoadAction))
        #1. 创建RecentProject 子菜单，依附在 Project 菜单的最后一项
        self.RecentProject = QMenu('recent Project', self)
        projectMenu.addMenu(self.RecentProject)        
        fileGroup = QActionGroup(self)
        #2.从《setting.ini》读入10个工程路径
        settings=QSettings("setting.ini",QSettings.IniFormat)
        settings.setIniCodec(QTextCodec.codecForName("utf-8"))
        filesize=  settings.beginReadArray("recentProject")
        if filesize is not None:
        #3. 每个 工程路径 就是一个动作项 Action
            for i in range(filesize):
                settings.setArrayIndex(i)
                fileAction = self.createAction(settings.value("File_Name"), None, None, None, 
                                                                'Open a project', checkable=True)
                self.RecentProject.addAction(fileAction)
                fileGroup.addAction(fileAction)
        settings.endArray()
        #4. 动作项触发信号链接到槽函数 OpenRecentProject
        self.RecentProject.triggered[QAction].connect(self.OpenRecentProject)
        
        #创建setting——menu 
        #       -----Chip-select    1]12C5A ;   2]15F2K;     3]8F2K
        #       -----Port-select     
        SettingMenu = self.menuBar().addMenu("&Setting")
        self.ChipMenu = QMenu('Chip', self)
        self.ChipMenu.setIcon(QIcon(":/{0}.png".format("chip")))
        SettingMenu.addMenu(self.ChipMenu)
        Chip1 = self.createAction(PROTOCOL_12C5A, self.ChangChip, None, None, 
                                            'STC12C5A08S2',checkable=True )  
        Chip2 = self.createAction(PROTOCOL_15F2K, self.ChangChip, None, None, 
                                            'STC15F2K60S2', checkable=True)  
        Chip3 = self.createAction(PROTOCOL_8F2K, self.ChangChip, None, None, 
                                            'STC8F2K32S2', checkable=True)          
        PortGroup = QActionGroup(self)  #QActionGroup 里面的项是互斥的，只能有一个被选中
        PortGroup.addAction(Chip1)
        PortGroup.addAction(Chip2)
        PortGroup.addAction(Chip3)
        self.addActions(self.ChipMenu, (Chip1, Chip2, Chip3))
        # 根据 保存的设置信息，将对应的芯片项打钩
        actions = self.ChipMenu.actions()
        self.Chipstr =  settings.value("Download/Chip" )
        if self.Chipstr is not None:
            for i in range(len(actions)):
                if actions[i].text() == self.Chipstr:
                    actions[i].setChecked(True)
                    break     
        #生成 串口子菜单
        self.PortMenu = QMenu('Port', self)
        self.PortMenu.setIcon(QIcon(":/{0}.png".format("com32")))
        DetectAct = self.createAction("&Detect Port", self.DetectPort, None, None, 
                                                            'Creat a project') 
        SettingMenu.addMenu(self.PortMenu)
        self.addActions(self.PortMenu,(DetectAct, None) )  
        #检测有效串口
        self.DetectPort() 
        # 根据 保存的设置信息，将对应的串口打钩
        actions = self.PortMenu.actions()
        self.Portstr =  settings.value("Download/Port" )
        if self.Portstr is not None:
            for i in range(2, len(actions)):
                if actions[i].text() == self.Portstr:
                    actions[i].setChecked(True)
                    break     
        #在设置菜单3中增加一个弹出对话框，用于选择下载方式：1]断电;2]默认指令;3]自定义指令
        SettingAction = self.createAction("Download way下载方式", self.OpenSettingDlg, None, None, 
                                                            '选择下载方式', checkable=False)
        self.downloadWay = settings.value("Download/way" )
        if self.downloadWay is None:
            self.downloadWay = '2'
        self.DIYString=settings.value("Download/baudrateIndex")
        if self.DIYString is not None:    #借用DIYString  
            self.baudIndex=int(self.DIYString)
        else:
            self.baudIndex = 1

        self.DIYString = settings.value("Download/DIY_Data" )
        if self.DIYString is None:
            self.DIYString = ''
        #在设置菜单4中增加【编译设置】对话框，用于设置：1]编译路径;2]外部ram大小 --xdata 从工程文件中读取
        complierSettingAction = self.createAction("complier编译设置", self.OpenComplierSettingDlg, None, None, 
                                                            '设置编译器路径和外部ram大小', checkable=False)
        colorSettingAction = self.createAction("颜色设置", self.OpenColorSettingDlg, None, None, 
                                                            '设置关键字颜色', checkable=False)
        self.addActions(SettingMenu,(SettingAction, complierSettingAction,colorSettingAction ))
        self.isComplierPathDefault=settings.value("Complier/Path/Default" )
        if self.isComplierPathDefault != None and self.isComplierPathDefault == '1':
            self.compilePath = self.Code51_dir
        else:
            self.compilePath =settings.value("Complier/Path/Input" )
            if self.compilePath is None:
                self.compilePath = self.Code51_dir 
        self.compileIncldue =  settings.value("Complier/Path/Include" )  
        if self.compileIncldue is None:
            self.compileIncldue = '' 
        self.isXdataDefault=settings.value("Complier/Xdata/Default" )
        if self.isXdataDefault is None:
            self.isXdataDefault = '1'
        
        #======菜单栏->工具->[1.转换文件编码格式；2.串口工具]
        ToolMenu = self.menuBar().addMenu("&Tool")
        toolEncodUTF8Action =self.createAction("&Encode to UTF8", self.encode2UTF8, None, None, 
                                                            'Creat a project')
        toolSerialAction =self.createAction("串口工具", self.OpenSerialDlg, None, None, 
                                                            '打开串口调试工具')
        self.addActions(ToolMenu, (toolEncodUTF8Action,toolSerialAction,  None))
        #===========================================================
        self.windowMenu = self.menuBar().addMenu("&Window")
        self.windowMenu.aboutToShow.connect(self.updateWindowMenu)
        fileToolbar = self.addToolBar("File")
        fileToolbar.setObjectName("FileToolbar")
        self.addActions(fileToolbar, (fileNewAction, fileOpenAction,
                                      fileSaveAction))
        editToolbar = self.addToolBar("Edit")
        editToolbar.setObjectName("EditToolbar")
        self.addActions(editToolbar, (editUndoAction, editRedoAction,editCopyAction, editCutAction,
                                      editPasteAction, editFindAction))
        #builtToolbar                             
        builtToolbar = self.addToolBar("Built")
        builtToolbar.setObjectName("BuiltToolbar")
        self.addActions(builtToolbar, (projectBuiltAction, projectLoadAction,
                                      projectBuiltLoadAction))

        self.autoOpenCOM=settings.value("Download/autoOpenCOM")
        if self.autoOpenCOM == None:
            self.autoOpenCOM = '0'
        #settings = QSettings()
        if settings.value("MainWindow/Geometry") or settings.value("MainWindow/State"):
            self.restoreGeometry(
                    QByteArray(settings.value("MainWindow/Geometry")))
            self.restoreState(
                    QByteArray(settings.value("MainWindow/State")))
        fontFamily=settings.value("Font/Family")
        if fontFamily == None:
            fontFamily='Courier'
        self.EditFont.setFamily(fontFamily)
        fsize = settings.value("Font/pointSize")
        if fsize !=None and fsize.isdigit():
            self.EditFont.setPointSize(int(fsize))
        else:    
            self.EditFont.setPointSize(12)
        fsize = settings.value("Font/isBold")
        if fsize =='1':
            self.EditFont.setBold(True)
            
        status = self.statusBar()
        status.setSizeGripEnabled(False)
        status.showMessage("Ready", 5000)
        #读取 style 颜色       
        for style in self.colorDict.keys():
            str1 = 'color/' + style
            str1 = settings.value(str1)
            if str1 is not None:
                self.colorDict[style] = str1
        # 给节点添加响应事件
        self.tree.doubleClicked.connect(self.onTreeDoubleClicked)
        
        self.updateWindowMenu()
        #self.setWindowTitle("Text Editor")    
        #给树状文件列表添加右键菜单
        self.tree.setContextMenuPolicy(Qt.CustomContextMenu)#开放右键策略
        self.tree.customContextMenuRequested.connect(self.OnTreeRightMenuShow)#关联右键槽函数  
        #给SciTextEdit 文档编辑窗口右键菜单 发过来的消息
        self.Jump2Func_Signal[str].connect(self.do_Jump2Function)#右键跳转到函数  
        self.Jump2IncludeFile_Signal[str].connect(self.do_Jump2IncludeFile)#右键跳转到包含文件
        #给builtOutput窗口添加双击响应
        self.text_browser.TextEdit2Click_Signal.connect(self.Jump2Error)  
        #串口工具-子窗口
        self.SerialDlg = Pyqt5_Serial()
     
        self.setGeometry(300, 300, 800, 600)
        self.setWindowTitle('Editor for STC51')    
        self.setObjectName('Editor4STC51') 
        self.setWindowIcon(QIcon(":/{0}.png".format("writeC51")))
        self.show()
        
    def OpenSerialDlg(self): 
        if self.SerialDlg.bIsRunning is False:#还没有打开“串口工具”，可以打开
            self.SerialDlg.bIsRunning=True
            self.SerialDlg.show()
        pass
        
    def OpenColorSettingDlg(self):
        SettingDlg = ColorDlg(self)
        #2.打开对话框    
        ret = SettingDlg.exec_()
        if not ret :
            return
        #3.保存设置
        settings=QSettings("setting.ini",QSettings.IniFormat)
        for style in self.colorDict.keys():
            str1 = 'color/' + style
            settings.setValue(str1, self.colorDict[style])        
        #4.将打开的文件关闭再重新打开    
        self.repaintMDI()
    def GB2313_to_utf8(self, fullfilename):  
        fdir,fename=os.path.split(fullfilename)
        newName=fdir+'/utf8-tmp.txt'
        try:
            f = open(fullfilename,'r', encoding="gb2312",  errors="strict")
            text=f.read()
        except :
            self.SetTipString("转换失败，源文件的编码格式不是gb2312。")
            f.close() 
            return
        newfile = codecs.open(newName,'w+', encoding="utf-8")
        newfile.write(text.encode("utf-8").decode( encoding="utf-8" ))
        newfile.flush()
        newfile.close()
        f.close()
        time.sleep(0.05)
        os.remove(fullfilename)     #删除源文件
        #   重命名新文件为旧文件名 os.rename(src , dst)
        try:
            os.rename( newName, fullfilename)
        except(FileNotFoundError):
            pass
            #logging.info("目录不存在")    
    def do_Jump2IncludeFile(self, str1):
        end = len(str1)-1
        start = 1
        if str1[0] == '"':  # "driver/STC8F.h"  可能1
            if str1[1]=='.': # "./driver/STC8F.h"   可能2
                start = 3
            elif str1[1]=="\\" or str1[1]=="/" :# "/driver/STC8F.h"     可能3
                start = 2
            else:
                pass            
            textEdit = self.mdi.activeSubWindow()
            textEdit=textEdit.widget()
            if textEdit is None :
                return
            _fdir,_fename=os.path.split(textEdit.filename)
            fileName = _fdir+'/'+str1[start:end]            
        elif  str1[0] == '<':   #系统的包含文字在编译器目录下的 '/include/mcs51/'
            _fdir,_fename=os.path.split(str1[1:end])
            if 'nt' in os.name:#winndows系统
                fileName = self.compilePath+'/include/mcs51/'+_fename 
            else:
                fileName = self.compileIncldue+'/include/mcs51/'+_fename 
        else:
            return
        self.loadFile(fileName)
    def do_Jump2Function(self, str1):
        #传入参数str1 为函数名
        for file in self.file_list:         #查找文件列表中的c文件
            if file[-2: ]=='.c' or  file[-2: ]=='.C' :  
                [dirname,filename]=os.path.split(file)
                _dict=self.fileFuncDict[filename]   #这个字典是存放文件名，函数名，函数所在行信息的。
                funcs=list(_dict.keys())
                for func in funcs:
                    if func == str1:    #1. 字典中的函数名和传参相等，则查找结束，跳转
                        for textEdit_MSW in self.mdi.subWindowList(): #1.1 遍历已打开文档，判断函数所在的文件是否已经打开
                            textEdit=textEdit_MSW.widget()
                            if textEdit.filename == file:
                                self.mdi.setActiveSubWindow(textEdit_MSW)
                                break
                        else:   #1.2   确认该文件还没有打开，需要打开
                            self.loadFile(file) 
                                    
                        textEdit=self.mdi.currentSubWindow().widget()
                        #2.跳转到该函数名所在的文件的行数
                        if textEdit != None:
                            # self.fileFuncDict[filename][str1] 就是行数，在字典中先查文件名，再查函数名
                            textEdit.setCursorPosition(self.fileFuncDict[filename][str1], 0 ) #设置光标的行数，列数
                            textEdit.setCaretLineBackgroundColor(Qt.lightGray)  #光标所在行显示灰色
                            textEdit.setFocus()                 #当前文档赋给焦点                            
                        return
    def encode2UTF8(self):  
        textEdit = self.mdi.activeSubWindow()
        textEdit=textEdit.widget()
        if textEdit is None :#or not isinstance(textEdit, QTextEdit)
            return
        #关闭文件 -->修改编码 --> 重新打开文件
        file = textEdit.filename
        if os.path.exists(file) ==False:
            return
        
        self.mdi.closeActiveSubWindow() #关闭窗口
        self.GB2313_to_utf8(file)            #修改编码格式
        self.loadFile(file)                         #打开文件
        pass
    def OpenComplierSettingDlg(self):
        if len(self.fullfile) ==0 or not isinstance(self.mycbp, Dom4cbp):
            self.SetTipString("工程没有打开，无法设置Xdata。请打开工程。")
            return
        settings=QSettings("setting.ini",QSettings.IniFormat)
        SettingDlg = complierSetting(self)
        #1.初始化设置,xdata的参数不是通用的，每个工程都可以不同，要保存在工程文件中。
        if self.isComplierPathDefault == '1':
            SettingDlg.rdb_pathDefault.setChecked(True)
        else:
            SettingDlg.rdb_pathSelect.setChecked(True)            
        SettingDlg.lineEdit_path.setText(settings.value("Complier/Path/Input" ))
        SettingDlg.lineEdit_include.setText(self.compileIncldue)
            #1.1 非windows系统，把check_include 打钩
        if 'nt' not in os.name :
            SettingDlg.check_incPath.setChecked(True)
        if self.isXdataDefault == '1':
            SettingDlg.rdb_xdataDefault.setChecked(True)
            SettingDlg.lineEdit_xdata.setEnabled(False)
        else:
            SettingDlg.radioButton.setChecked(True)
        SettingDlg.lineEdit_xdata.setText(self.XdataLen)
        #2.打开对话框    
        ret = SettingDlg.exec_()
        if not ret :
            return
        #3.保存设置
        if SettingDlg.rdb_pathDefault.isChecked():
            self.isComplierPathDefault = '1'
        else:
            self.isComplierPathDefault = '0'            
        settings.setValue("Complier/Path/Default",self.isComplierPathDefault)
        self.compilePath=SettingDlg.lineEdit_path.text()
        self.compileIncldue =  SettingDlg.lineEdit_include.text()
        settings.setValue("Complier/Path/Input",self.compilePath)
        settings.setValue("Complier/Path/Include",self.compileIncldue) 
        if SettingDlg.rdb_xdataDefault.isChecked():
            self.isXdataDefault = '1'
            SettingDlg.lineEdit_xdata.setText('8192')
        else:
            self.isXdataDefault = '0'
        self.XdataLen = SettingDlg.lineEdit_xdata.text()
        settings.setValue("Complier/Xdata/Default",self.isXdataDefault)
        #settings.setValue("Complier/Xdata/Input",SettingDlg.lineEdit_xdata.text())
        #写人工程文件
        self.mycbp.SetOptionOfXram(self.XdataLen)
        
    def OpenSettingDlg(self):
        SettingDlg = Setting()
        #1. 初始化对话框参数
        if self.downloadWay == '1' :
            SettingDlg.radioButton_1.setChecked(True)
        elif self.downloadWay == '2' :
            SettingDlg.radioButton_2.setChecked(True)
        else :
            SettingDlg.radioButton_3.setChecked(True)
        SettingDlg.lineEdit.setText(self.DIYString)
        SettingDlg.comboBox.setCurrentIndex(self.baudIndex)
        SettingDlg.checkBox.setChecked(self.autoOpenCOM=='1')
        #2.显示对话框    
        ret = SettingDlg.exec_()
        #3. 对话框按下 OK 时，保存设置改动
        if ret:
            settings=QSettings("setting.ini",QSettings.IniFormat)
            if SettingDlg.radioButton_1.isChecked():
                self.downloadWay = '1'
                settings.setValue("Download/way" , self.downloadWay)
            elif SettingDlg.radioButton_2.isChecked():
                self.downloadWay = '2'
                settings.setValue("Download/way" , self.downloadWay)
            else :
                self.downloadWay = '3'
                settings.setValue("Download/way" , self.downloadWay)
            if SettingDlg.checkBox.isChecked():
                self.autoOpenCOM='1'
            else:
                self.autoOpenCOM='0'
            settings.setValue("Download/autoOpenCOM" ,self.autoOpenCOM)
            self.DIYString=SettingDlg.lineEdit.text()
            self.baudIndex=SettingDlg.comboBox.currentIndex()
            settings.setValue("Download/DIY_Data" ,self.DIYString)
            settings.setValue("Download/baudrateIndex", self.baudIndex)
    def Jump2Error(self):
        tc =self.text_browser.textCursor()
        str1 = tc.block().text()      
        for filename in self.file_list:
            if filename in str1:#找到错误行中的文件名，判断是否已经打开
                for textEdit_MSW in self.mdi.subWindowList():
                    textEdit=textEdit_MSW.widget()
                    if textEdit.filename == filename:
                        self.mdi.setActiveSubWindow(textEdit_MSW)
                        break
                else:
                    self.loadFile(filename) 
                
                str2 = str1[len(filename)+1:]   # 错误提示格式：文件名+‘:'+行数+':'+错误信息，所有文件名长度要加1
                split_list=str2.split(':', 1) 
                textEdit=self.mdi.currentSubWindow().widget()
                if textEdit != None:
                    textEdit.setCursorPosition(int(split_list[0])-1, 0 )
                    textEdit.setFocus()
                break

    def OnTreeRightMenuShow(self, point):    #当点击“source”和“header” 显示添加文件菜单；点击具体文件名时，显示删除文件菜单
        item=self.tree.currentItem()    #获取鼠标所在的树状列表的项
        self.tree.popMenu = QMenu()
        if '.' in  item.text(0) and '.cbp' not in item.text(0):#该项是文件名，添加右键[删除菜单]
            delectFile4Proj=QAction('Delect file from project', self)
            self.tree.popMenu.addAction(delectFile4Proj)
            delectFile4Proj.triggered.connect(self.do_delectFile4Proj)
        else:       #该项非文件名，添加右键[添加菜单]
            addFile2Proj=QAction('Add file to project', self)
            self.tree.popMenu.addAction(addFile2Proj)
            addFile2Proj.triggered.connect(self.do_addFile2Proj)
        self.tree.popMenu.exec_(QCursor.pos()) #在鼠标位置显示
        
    def do_delectFile4Proj(self):     
        item=self.tree.currentItem()
        #1.工程文件中删除此文件
        self.mycbp.delectFileFromCbp(item.text(0))
        #2.从文件列表中删除
        for i in range(len(self.file_list)):
            if item.text(0) in self.file_list[i]:
                del self.file_list[i]
                break
        #3.从窗口中删除
        if item.parent() is not None:
            item.parent().removeChild(item)
        #函数字典中删除,如果真的要删除，必须确保字典中包含了这个项目
        #del self.fileFuncDict[item.text(0)]
    def do_addFile2Proj(self):     #先把文件名添加到界面，再写入cbp文件      
        fullfile,filetype = QFileDialog.getOpenFileName(self, "Select source or header file",
                                                self.ProjectDir,"files(*.c *.h)")
        if not fullfile:
            return
        for file in self.file_list:
            if file == fullfile:    #文件已经存在于工程中，添加失败
                self.text_browser.append("文件已经存在于工程中,不要重复添加。")
                return
        item=self.tree.currentItem() #os.path.basename(fullfile)        
        childItem = QTreeWidgetItem(item)
        if fullfile[-1] =='c' or fullfile[-1] =='C':
            childItem.setIcon(0,QIcon(":/{0}.png".format("Cfile")))
        else:
            childItem.setIcon(0,QIcon(":/{0}.png".format("Hfile")))
        childItem.setText(0, os.path.basename(fullfile))
        self.file_list.append(fullfile)                #1】.向文件列表增加文件
        self.mycbp.AddFile2cbp(fullfile)          #2】向工程文件增加文件子项
        
                                                            #3】如果是.c  文件生成函数列表
        if fullfile[-1] =='c' or fullfile[-1] =='C':
            c_getfunc =lexer_c_getfunc(self.file_list[-1])
            _fname, _dict= c_getfunc.lexer_analysis()
            self.fileFuncDict.setdefault(_fname, _dict)
            funcs=list(_dict.keys())
            for func in funcs:      #将函数名添加到对应的文件名下的子项
                subsubchild = QTreeWidgetItem(childItem)
                subsubchild.setText(0,func)
                subsubchild.setIcon(0,QIcon(":/{0}.png".format("func2")))
        #self.mycbp
    def setMdiTabbedWindow(self):
        self.mdi.setViewMode(QMdiArea.TabbedView) #Tab多页显示模式
        self.mdi.setTabsClosable(1)#页面可关闭
        self.actCascade.setEnabled(0)
        self.actTile.setEnabled(0)
        
    def createAction(self, text, slot=None, shortcut=None, icon=None,
                     tip=None, checkable=False, signal="triggered()"):
        action = QAction(text, self)
        if icon is not None:
            action.setIcon(QIcon(":/{0}.png".format(icon)))
        if shortcut is not None:
            action.setShortcut(shortcut)
        if tip is not None:
            action.setToolTip(tip)
            action.setStatusTip(tip)
        if slot is not None:
            action.triggered.connect(slot)
        if checkable:
            action.setCheckable(True)
        return action

    def addActions(self, target, actions):
        for action in actions:
            if action is None:
                target.addSeparator()
            else:
                target.addAction(action)

    def closeEvent(self, event):
        if self.SerialDlg.bIsRunning :  # 如果串口工具已经打开，请关闭
            e = QCloseEvent()
            self.SerialDlg.closeEvent(e)
            pass
        failures = []
        for textEdit in self.mdi.subWindowList():
            textEdit=textEdit.widget()
            if textEdit.isModified():
                try:
                    textEdit.save()
                except IOError as e:
                    failures.append(str(e))
        if (failures and
            QMessageBox.warning(self, "Text Editor -- Save Error",
                    "Failed to save{0}\nQuit anyway?".format(
                    "\n\t".join(failures)),
                    QMessageBox.Yes|QMessageBox.No) ==
                    QMessageBox.No):
            event.ignore()
            return
        #settings = QSettings()        
        settings=QSettings("setting.ini",QSettings.IniFormat)
        settings.setIniCodec(QTextCodec.codecForName("utf-8"))
        settings.setValue("MainWindow/Geometry",   self.saveGeometry())
        settings.setValue("MainWindow/State",       self.saveState())
        self.mdi.closeAllSubWindows()


    def loadFiles(self):
        if len(sys.argv) > 1:
            for filename in sys.argv[1:31]: # Load at most 30 files
                filename = filename
                if QFileInfo(filename).isFile():
                    self.loadFile(filename)
                    QApplication.processEvents()
        else:
            settings = QSettings()
            #files = settings.value("CurrentFiles").toStringList()
            if settings.value("CurrentFiles"):
                files=settings.value("CurrentFiles")
                for filename in files:
                    filename = filename
                    if QFile.exists(filename):
                        self.loadFile(filename)
                        QApplication.processEvents()

    def fileNew(self):
        textEdit = SciTextEdit('', self)
        self.mdi.addSubWindow(textEdit)
        textEdit.show()

    def fileOpen(self):
        filename,filetype = QFileDialog.getOpenFileName(self,
                "Text Editor -- Open File")
        if filename:
            for textEdit_MSW in self.mdi.subWindowList():
                textEdit=textEdit_MSW.widget()
                if textEdit.filename == filename:
                    self.mdi.setActiveSubWindow(textEdit_MSW)
                    break
            else:
                self.loadFile(filename) 

    def loadFile(self, filename):
        textEdit = SciTextEdit(filename, self)
        try:
            textEdit.load()
        except EnvironmentError as e:
            QMessageBox.warning(self, "Text Editor -- Load Error",
                    "Failed to load {0}: {1}".format(filename, e))
            textEdit.close()
            del textEdit
        else:
            self.mdi.addSubWindow(textEdit)
            textEdit.show()

    def fileSave(self):
        try:
            textEdit = self.mdi.activeSubWindow()
            textEdit=textEdit.widget()
            if textEdit is None or not isinstance(textEdit, SciTextEdit):
                return True
            try:
                textEdit.save()
                textEdit.setModified(False)
                return True
            except EnvironmentError as e:
                QMessageBox.warning(self, "Text Editor -- Save Error",
                        "Failed to save {0}: {1}".format(textEdit.filename, e))
                return False
        except:
            QMessageBox.warning(self,"WARN",'222')


    def fileSaveAs(self):
        try:
            textEdit = self.mdi.activeSubWindow()
            textEdit=textEdit.widget()
            if textEdit is None or not isinstance(textEdit, QTextEdit):
                return
            filename,filetype = QFileDialog.getSaveFileName(self,
                            "Text Editor -- Save File As",
                            textEdit.filename, "Text files (*.txt *.*)")
            if filename:
                textEdit.filename = filename
                return self.fileSave()
            return True
        except:
            QMessageBox.warning(self,"WARN",'111')

    def fileSaveAll(self):
        errors = []
        for textEdit in self.mdi.subWindowList():
            textEdit=textEdit.widget()
            if textEdit.isModified():
                try:
                    textEdit.save()
                except EnvironmentError as e:
                    errors.append("{0}: {1}".format(textEdit.filename, e))
        if errors:
            QMessageBox.warning(self,
                    "Text Editor -- Save All Error",
                    "Failed to save\n{0}".format("\n".join(errors)))

    def editUndo(self):
        textEdit = self.mdi.activeSubWindow()
        textEdit=textEdit.widget()
        if textEdit is None :#or not isinstance(textEdit, QTextEdit)
            return
        textEdit.SendScintilla(textEdit.SCI_UNDO, 0, 0)
    def editRedo(self):
        textEdit = self.mdi.activeSubWindow()
        textEdit=textEdit.widget()
        if textEdit is None :#or not isinstance(textEdit, QTextEdit)
            return
        textEdit.SendScintilla(textEdit.SCI_REDO, 0, 0)
    def editCopy(self):
        textEdit = self.mdi.activeSubWindow()
        textEdit=textEdit.widget()
        if textEdit is None :#or not isinstance(textEdit, QTextEdit)
            return
        textEdit.SendScintilla(textEdit.SCI_COPY, 0, 0)

    def editCut(self):
        textEdit = self.mdi.activeSubWindow()
        textEdit=textEdit.widget()
        if textEdit is None :#or not isinstance(textEdit, QTextEdit)
            return
        textEdit.SendScintilla(textEdit.SCI_CUT, 0, 0)

    def editPaste(self):
        textEdit = self.mdi.activeSubWindow()
        textEdit=textEdit.widget()
        if textEdit is None :#or not isinstance(textEdit, QTextEdit)
            return
        textEdit.SendScintilla(textEdit.SCI_PASTE, 0, 0)
    #空操作        
    def editFind(self):#
        #1. 获取Mdi的焦点窗口
        textEdit = self.mdi.activeSubWindow()
        if textEdit is None :
            return     
        #2. 获取窗口里面的 SciTextEdit   
        textEdit=textEdit.widget()
        if textEdit is None :
            return
        #3. 初始化 查找窗口，传入SciTextEdit 中选中的文字 
        FindDlg = FindText(self)
        FindDlg.lineEdit_destText.setText(textEdit.selectedText())
        #4. 显示窗口
        FindDlg.exec_()
        
    def DetectPort(self):          
        #1]检测所有存在的串口，将信息存储在字典中
        PortGroup = QActionGroup(self)
        port_list = list(serial.tools.list_ports.comports())
        actions = self.PortMenu.actions()
        #2]self.PortMenu.保留前2项，其它清空
        for i in range(2, len(actions)):
            self.PortMenu.removeAction(actions[i])           
        #3]将port_list 添加到菜单中
        for port in port_list:
            portAction = self.createAction(port[0], self.ChangPort, None, None, 
                                                            '选择下载口', checkable=True)
            self.PortMenu.addAction(portAction)
            PortGroup.addAction(portAction) 
    def ChangChip(self):    #选择芯片就是选择下载协议 
        actions = self.ChipMenu.actions()
        for i in range(len(actions)):
            if actions[i].isChecked():
                self.Chipstr = actions[i].text()
                settings=QSettings("setting.ini",QSettings.IniFormat)
                settings.setIniCodec(QTextCodec.codecForName("utf-8"))
                settings.setValue("Download/Chip",self.Chipstr )
                break        
    def ChangPort(self): 
        actions = self.PortMenu.actions()  
        for i in range(2, len(actions)):
            if actions[i].isChecked():
                #logging.info('%s'%actions[i].text())
                self.Portstr = actions[i].text()
                settings=QSettings("setting.ini",QSettings.IniFormat)
                settings.setIniCodec(QTextCodec.codecForName("utf-8"))
                settings.setValue("Download/Port", self.Portstr)
        pass
    def OpenRecentProject(self, qaction):  
        #1. 已打开了工程，先关闭
        if len(self.fullfile) !=0:
            self.ProjectClose()
        #2. 获取工程完整路径
        self.fullfile = qaction.text()
        if os.path.exists(self.fullfile)==None:
            self.SetTipString("工程文件名不存在。打开失败。")
        #3. 打开工程
        self.OpenProjectByName()
        
    def ProjectBuiltLoad(self):
        if self.ProjectBuilt() == True:
            self.ProjectDownload()
    def ProjectBuilt(self):
        self.fileSaveAll()
        if len(self.fullfile) == 0 or os.path.exists(self.fullfile) == False:
            self.text_browser.setText('工程无效！')
            return False
        self.compilePro = compileProject(self)
        self.compilePro.projectClean()
        self.compilePro.CreatObjFolder()
        if self.compilePro.complieAllFile() == True:
            if self.compilePro.LinkAllFile() == True:
                self.Hexfile=self.compilePro.ihx2hex()
                if self.Hexfile == None:
                    return False
        return True
    def ProjectClean(self):
        if self.fullfile == None:
            self.text_browser.setText('工程无效！')
        self.compilePro.projectClean()
        pass
    def Init_QTreeWidgetItem(self):
        root=QTreeWidgetItem(self.tree)
        [dirname,filename]=os.path.split(self.fullfile)
        root.setText(0,filename)
        root.setIcon(0,QIcon(":/{0}.png".format("Project")))
        #2.创建一级子节点source和 header        
        child1=QTreeWidgetItem(root)
        child1.setText(0,'source')          
        child1.setIcon(0,QIcon(":/{0}.png".format("Folder")))
        
        child2=QTreeWidgetItem(root)
        child2.setText(0,'header')    
        child2.setIcon(0,QIcon(":/{0}.png".format("Folder")))
        pass
    def ProjectClose(self):#1.关闭所有窗口；2.清空树状列表；3.工程名，文件列表清空
        self.closeEvent(QEvent.Close)
        self.tree.clear()
        self.fullfile=''
        self.file_list.clear()
        pass
    def ProjectNew(self):
        self.fullfile,filetype = QFileDialog.getSaveFileName(self, "Select project file",
                                                self.Code51_dir,'project ( *.cbp )')
        self.mycbp=Dom4cbp(self, self.fullfile)
        self.mycbp.CreatNewCbp()  
        self.Init_QTreeWidgetItem()
        #self.tree.expandAll()   
        self.tree.expandToDepth(2)
    def OpenProjectByName(self): 
        global g_allFuncList
        global g_includeList
        if len(self.fullfile) ==0:
            return
        if  os.path.exists(self.fullfile) == None:
            QMessageBox.warning(self,"警告",'工程文件 %s不存在，请检测文件路径...'%self.fullfile)
            return
        g_allFuncList.clear()    
        self.mycbp=Dom4cbp(self, self.fullfile)
        #1设置根节点(project name)
        root1=QTreeWidgetItem(self.tree)
        [self.ProjectDir,filename]=os.path.split(self.fullfile)
        root1.setText(0,filename)
        root1.setIcon(0,QIcon(":/{0}.png".format("Project")))
        #2.创建一级子节点source和 header        
        child1=QTreeWidgetItem(root1)
        child1.setText(0,'source')        
        child1.setIcon(0,QIcon(":/{0}.png".format("Folder")))
        
        child2=QTreeWidgetItem(root1)
        child2.setText(0,'header')        
        child2.setIcon(0,QIcon(":/{0}.png".format("Folder")))
        #3.从工程文件中读入文件列表 及 --xram-size 的参数        
        self.file_list,self.XdataLen  = self.mycbp.getFileList_xdata_In_cbp()
        for str1 in self.file_list: #遍历所有文件，c文件和h文件分开处理
            #3.1 c文件，加入一级节点source
            if str1[-2: ]=='.c' or  str1[-2: ]=='.C' :  
                subchild1=QTreeWidgetItem(child1)
                subchild1.setIcon(0,QIcon(":/{0}.png".format("Cfile")))
                subDirect, _fname=os.path.split(str1)
                subchild1.setText(0,_fname)
                #3.1.1] C 文件还要添加函数名作为三级节点 
                c_getfunc =lexer_c_getfunc(str1)
                _fname, _dict= c_getfunc.lexer_analysis()
                self.fileFuncDict.setdefault(_fname, _dict)
                funcs=list(_dict.keys())
                for func in funcs:
                    subsubchild = QTreeWidgetItem(subchild1)
                    subsubchild.setText(0,func)
                    subsubchild.setIcon(0,QIcon(":/{0}.png".format("func2")))
            #3.2 h文件，加入一级节点header
            elif str1[-2: ]=='.h' or  str1[-2: ]=='.H' :  
                subchild1=QTreeWidgetItem(child2)
                subchild1.setIcon(0,QIcon(":/{0}.png".format("Hfile")))
                subDirect, _fname=os.path.split(str1)
                subchild1.setText(0,_fname)
        #3.3 将系统头文件也包含进列表
        for str1 in  g_includeList:
            _fdir,_fename=os.path.split(str1)
            #if 'nt' in os.name:#winndows系统
            if self.isComplierPathDefault == '1':
                fileName = self.compilePath+'/sdcc/include/mcs51/'+_fename 
            else:
                fileName = self.compileIncldue+'/include/mcs51/'+_fename 
            self.file_list.append(fileName) 
            subchild1=QTreeWidgetItem(child2)
            subchild1.setIcon(0,QIcon(":/{0}.png".format("Hfile")))
            subchild1.setText(0,_fename)
            pass
        self.tree.expandToDepth(1)
        #4.将当前工程写入setting.ini        
        settings=QSettings("setting.ini",QSettings.IniFormat)  
        settings.setIniCodec(QTextCodec.codecForName("utf-8"))
        filesize=  settings.beginReadArray("recentProject")
        fileList=[]
        for i in range(filesize):
            settings.setArrayIndex(i)
            if self.fullfile != settings.value("File_Name"):
                fileList.append(settings.value("File_Name"))
        settings.endArray()
                
        fileList.insert(0, self.fullfile)   #本工程将位于菜单Project->recentProject列表的第1位        
        filesize =min(10,  len(fileList)) #只记录最近打开的10个工程        
        settings.beginWriteArray("recentProject", filesize)        
        for i in range(filesize):
            settings.setArrayIndex(i)
            settings.setValue("File_Name", fileList[i])
        settings.endArray()
        #5.初始化 HEX文件名
        self.Hexfile = self.ProjectDir+'/obj/'+os.path.splitext(filename)[0]+'.hex'
        pass
    def ProjectOpen(self):
        if len(self.fullfile) !=0:
            self.ProjectClose()
        self.fullfile,filetype = QFileDialog.getOpenFileName(self, "Select project file",
                                                None,'project ( *.cbp )', None, QFileDialog.DontUseNativeDialog)
        self.OpenProjectByName()       
     
    def onTreeDoubleClicked(self,qmodeLindex):     #对工程列表中的文件双击，以打开c或者h文件   
        item=self.tree.currentItem()
        #logging.info(item.parent().text(0))
        #1.双击文件名
        if item.parent().text(0) =='source' or item.parent().text(0) =='header':
            for filename in self.file_list:
                if item.text(0) in filename:#找到在文件列表中的位置后，判断是否已经打开
                    for textEdit_MSW in self.mdi.subWindowList():
                        textEdit=textEdit_MSW.widget()
                        if textEdit.filename == filename:
                            self.mdi.setActiveSubWindow(textEdit_MSW)
                            break
                    else:
                        self.loadFile(filename) 
                    return
                #logging.info(item1)
        #2.双击函数名
        elif '.c' in item.parent().text(0) or '.C' in item.parent().text(0):
            for filename in self.file_list:
                if item.parent().text(0) in filename:#找到在文件列表中的位置后，判断是否已经打开
                    for textEdit_MSW in self.mdi.subWindowList():
                        textEdit=textEdit_MSW.widget()
                        if textEdit.filename == filename:
                            self.mdi.setActiveSubWindow(textEdit_MSW)
                            break
                    else:
                        self.loadFile(filename) 
                    break
            pass
            textEdit=self.mdi.currentSubWindow().widget()
            if textEdit != None:#3.跳转到该函数
                textEdit.setCursorPosition(self.fileFuncDict[item.parent().text(0) ][item.text(0)], 0 )
                textEdit.setCaretLineBackgroundColor(Qt.lightGray)
                #textEdit.show()
                textEdit.setFocus()
    def windowRestoreAll(self):
        for textEdit in self.mdi.subWindowList():
            textEdit=textEdit.widget()
            textEdit.showNormal()

    def windowMinimizeAll(self):
        for textEdit in self.mdi.subWindowList():
            textEdit=textEdit.widget()
            textEdit.showMinimized()
    def showProjectWindow(self):
        if self.dockProject.isVisible():
            self.windowShowProjectAction.setChecked(False)
            return
        self.dockProject.show()
    def showBuiltWindow(self):
        if self.dockBuilt.isVisible():
            self.windowShowBuiltAction.setChecked(False)
            return
        self.dockBuilt.show()
        
    def updateWindowMenu(self):
        self.windowMenu.clear()        
        if self.dockProject.isVisible():          #判断 工程列表视图 是否可见
            self.windowShowProjectAction.setChecked(True)
        else:
            self.windowShowProjectAction.setChecked(False)   
        if self.dockBuilt.isVisible():          #判断 编译结果视图 是否可见
            self.windowShowBuiltAction.setChecked(True)
        else:
            self.windowShowBuiltAction.setChecked(False)
            
        self.addActions(self.windowMenu, (self.windowNextAction,
                self.windowPrevAction, self.windowCascadeAction,
                self.windowTileAction, self.windowRestoreAction,
                self.windowMinimizeAction,
                self.windowArrangeIconsAction, None,
                self.windowShowProjectAction, self.windowShowBuiltAction, 
                self.windowCloseAction, None))
        textEdits = self.mdi.subWindowList()
        if not textEdits:
            return
        self.windowMenu.addSeparator()
        i = 1
        menu = self.windowMenu
        for textEdit_MSW in textEdits:
            textEdit=textEdit_MSW.widget()
            title = textEdit.windowTitle()
            if i == 10:
                self.windowMenu.addSeparator()
                menu = menu.addMenu("&More")
            accel = ""
            if i < 10:
                accel = "&{0} ".format(i)
            elif i < 36:
                accel = "&{0} ".format(chr(i + ord("@") - 9))

            action = menu.addAction("{0}{1}".format(accel, title))
            self.windowMapper.setMapping(action, textEdit_MSW)
            action.triggered.connect(self.windowMapper.map)
            i += 1
               
    def hex2bin(self,  code):
        buf = bytearray()
        base = 0
        line = 0

        for rec in code.splitlines():
            # Calculate the line number of the current record
            line += 1

            try:    # bytes(...) is to support python<=2.6    # bytearray(...) is to support python<=2.7bytesbytes
                n = bytearray(binascii.a2b_hex((rec[1:3])))[0]
                dat = bytearray(binascii.a2b_hex((rec[1:n*2+11])))
            except:
                MsgStr = "Line %d: Invalid format" % line
                QMessageBox.information(self, "标题", MsgStr, QMessageBox.Yes | QMessageBox.No ,  QMessageBox.Yes ) 
                #raise Exception("Line %d: Invalid format" % line)

            if rec[0] != ord(":"):
                MsgStr = "Line %d: Missing start code \":\""% line
                QMessageBox.information(self, "标题", MsgStr, QMessageBox.Yes | QMessageBox.No ,  QMessageBox.Yes ) 
                
            if sum(dat) & 0xFF != 0:
                MsgStr = "Line %d: Incorrect checksum" % line
                QMessageBox.information(self, "标题", MsgStr, QMessageBox.Yes | QMessageBox.No ,  QMessageBox.Yes )                 
                
            if dat[3] == 0:      # Data record
                addr = base + (dat[1] << 8) + dat[2]
                # Allocate memory space and fill it with 0xFF
                buf[len(buf):] = [0xFF] * (addr + n - len(buf))
                # Copy data to the buffer
                buf[addr:addr+n] = dat[4:-1]

            elif dat[3] == 1:    # EOF record
                if n != 0:
                    raise Exception("Line %d: Incorrect data length" % line)

            elif dat[3] == 2:    # Extended segment address record
                if n != 2:
                    raise Exception("Line %d: Incorrect data length" % line)
                base = ((dat[4] << 8) + dat[5]) << 4

            elif dat[3] == 4:    # Extended linear address record
                if n != 2:
                    raise Exception("Line %d: Incorrect data length" % line)
                base = ((dat[4] << 8) + dat[5]) << 16

            else:
                raise Exception("Line %d: Unsupported record type" % line)

        return buf
    def ProjectDownload(self): #  self.Hexfile,Chipstr,Portstr
        if self.SerialDlg.bIsOpened :
            self.SerialDlg.port_close()
        if self.Hexfile is None or not os.path.exists(self.Hexfile):
            self.SetTipString( '找不到HEX文件。终止操作。') 
            return
        if self.Chipstr is None:
            self.SetTipString( '没有选择芯片。终止操作。') 
            return
        if self.Portstr is None:
            self.SetTipString( '没有选择串口。终止操作。') 
            return
        try:
            fo = open(self.Hexfile, 'rb')
        except IOError:    
            QMessageBox.warning(self, "Open file Error",
                        "Failed to open {0}: {1}".format(self.Hexfile))
        
        try:
            str0 = (fo.read())
            fo.close()        
        except:
            self.SetTipString( '打开文件失败.') 
            return
        #if os.path.splitext(self.Hexfile)[1] in (".hex", ".ihx"): HEX  -->  BIN
        str0 = self.hex2bin( str0)        
        self.SetTipString( '打开文件成功.')  
        
        try:
            with serial.Serial(port=self.Portstr,
                                   baudrate=2400,
                                   parity=serial.PARITY_NONE) as conn:
                self.MyProgram(Programmer(self, conn,self.Chipstr), str0, None)
        except IOError:
            self.SetTipString("打开串口 {0} 失败".format(self.Portstr))
            
        #检测是否打开串口
        if self.SerialDlg.bIsRunning and self.autoOpenCOM =='1':
            self.SerialDlg.port_open()
    def SetTipString(self, str1):
        self.text_browser.append("<font color=block>%s </font> " %str1)        
        QApplication.processEvents()
    def MyPrint(self, prog):
        self.SetTipString(" FOSC: %.3fMHz" % prog.fosc)
        self.SetTipString(" Model: %s (ver%s) " % (prog.name, prog.version))
        if prog.romsize is not None:
            self.SetTipString(" ROM: %dKB" % prog.romsize)        
        
    def  MyProgram(self, prog, code, erase_eeprom=None):   
        #下面的语句是打印调试信息到 《test.log》文件，有需要就取消注释
        #logging.basicConfig(filename="test.log", filemode="w", 
        #                            format="%(asctime)s %(name)s:%(levelname)s:%(message)s", 
        #                            datefmt="%d-%M-%Y %H:%M:%S", level=logging.DEBUG)
        #0.判断是否发送下载指令
        if self.downloadWay == '2':
            prog.sendISPcmd(self.baudArray[self.baudIndex])
        elif self.downloadWay == '3':
            prog.sendISPcmd(self.baudArray[self.baudIndex], self.DIYString)
        #1.开始检测芯片
        self.SetTipString( '正在检测 MCU,请给芯片上电。'   )
        ok=prog.detect()        
        if not ok:
            self.SetTipString("检测芯片失败. 请重试...")
            return
        self.SetTipString("检测芯片完成. done")    
        self.Mylogging.info_info(prog) #打印检测芯片的结果：1 】晶振频率；2】芯片型号；3】ROMM
        
        if prog.protocol is None:
            self.SetTipString("目标芯片无法识别. 终止下载。")
            return       
        if code is None:
            self.SetTipString("下载文件内容为空。 终止下载。")
            return
        prog.comfirm_MCU_model() 
        #2.建立更到波特率       
        self.SetTipString("尝试更高波特率...")
        prog.handshake()      
        self.SetTipString("波特率为 %d " % prog.baudrate)
        
        #3.擦除芯片
        prog.prepare_for_erase()
        self.SetTipString("开始擦除芯片......" )
        prog.erase()
        self.SetTipString("擦除芯片成功。")
        
        #4.开始写入程序数据
        self.SetTipString("开始写入，程序数据大小为%d bytes... " %  len(code))
        oldbar = 0
        for progress in prog.flash(code):
            bar = progress
            self.SetTipString("#%d -  %d bytes 已完成..." %( oldbar,bar ))
            oldbar = bar       
        self.SetTipString("全部写入完毕，运行程序...") 

        prog.unknown_packet_3()
        #5.结束整个流程
        prog.terminate()
        pass
    def repaintMDI(self):
        #将打开的文件关闭再重新打开
        winlist=[]
        textEditWin = self.mdi.activeSubWindow() #记录当前窗口
        #1.关闭所有窗口，并记录文件名
        for textEdit_MSW in self.mdi.subWindowList():
            textEdit=textEdit_MSW.widget()
            winlist.append( textEdit.filename)
            if textEdit.isModified():
                try:
                    textEdit.save()
                except IOError as e:
                    failures.append(str(e))
        self.mdi.closeAllSubWindows()
        #2.打开所有刚刚关闭的窗口
        for filename in winlist:            
            self.loadFile(filename)
        #3.设置之前的活动窗口为当前活动窗口
        for textEdit_MSW in self.mdi.subWindowList():
            textEdit=textEdit_MSW.widget()    
            if textEditWin.widget().filename == textEdit.filename:
                self.mdi.setActiveSubWindow(textEdit_MSW)
                break
        
    def getFont(self):
        font,ok=QFontDialog.getFont(self.EditFont)
        if ok:
            self.EditFont=font
            #settings = QSettings()        
            settings=QSettings("setting.ini",QSettings.IniFormat)
            settings.setIniCodec(QTextCodec.codecForName("utf-8"))
            settings.setValue("Font/Family",   font.family())
            settings.setValue("Font/pointSize",       font.pointSize())
            if font.bold():
                settings.setValue("Font/isBold",       '1')
            else:
                settings.setValue("Font/isBold",       '0')
            #将打开的文件关闭再重新打开    
            self.repaintMDI()
if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.setWindowIcon(QIcon(":/icon.png"))
    app.setOrganizationName("Qtrac Ltd.")
    app.setOrganizationDomain("qtrac.eu")
    app.setApplicationName("Text Editor")
    form = MainWindow()
    form.show()
    app.exec_()

'''                          

        #sys.stdout.write("Setting options...")        sys.stdout.flush()
        #if prog.options(erase_eeprom=erase_eeprom):
        #    logging.info(" done")
        #else:
        #    logging.info(" failed")
        #5.结束整个流程
'''        
